/**
 * 
 */
package com.googlecode.eventio.annotation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.googlecode.eventio.Reaction;

/**
 * @author steph
 * 
 */
public class AnnotationReactUtils {

	private final Map<Class<?>, Method> reactMethodCache = new ConcurrentHashMap<Class<?>, Method>();

	private static final AnnotationReactUtils INSTANCE = new AnnotationReactUtils();

	public static <T> Reaction<T> getReaction(final Object obj) {

		final Method reactMethod = INSTANCE.getReactMethod(obj);

		return new Reaction<T>() {
			@Override
			public void react(T input) {
				try {
					reactMethod.invoke(obj, input);
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			};
		};

	}

	private Method getReactMethod(Object reaction) {
		Method reactMethod = reactMethodCache.get(reaction.getClass());

		if (reactMethod == null) {
			reactMethod = findReactMethod(reaction);
			reactMethodCache.put(reaction.getClass(), reactMethod);
		}

		return reactMethod;
	}

	private Method findReactMethod(Object reaction) {
		Method[] methods = reaction.getClass().getMethods();

		Method findReactMethod = null;
		for (Method method : methods) {
			if (method.getAnnotation(React.class) != null) {

				if (findReactMethod != null) {
					throw new RuntimeException("Multiple method with annotation @React.");
				}
				findReactMethod = method;
			}
		}

		if (findReactMethod == null) {
			throw new RuntimeException("No react method with annotation @React.");
		}
		return findReactMethod;
	}

}
